package com.goldfish666.remote_control.service.base;

import com.goldfish666.remote_control.exception.BizException;
import com.goldfish666.remote_control.exception.DataNotFoundException;
import com.goldfish666.remote_control.repository.base.RepositoryWithKey;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
import java.util.stream.Collectors;

public abstract class AbstractComplexJapService<TDto, TEntity, TRepository extends RepositoryWithKey<TEntity>>
        extends AbstractSimpleJpaService<TEntity, TRepository>
        implements ComplexServiceWithKey<TDto, TEntity> {

    @PersistenceContext
    protected EntityManager entityManager;

    protected JPAQueryFactory queryFactory;

    @PostConstruct
    protected void init() {
        queryFactory = new JPAQueryFactory(entityManager);
    }

    @Override
    public TDto insertDto(TDto dto) {
        TEntity entity = dtoToEntity(dto);
        entity = (TEntity) recurseChildrenParent(entity);
        TEntity saved = insert(entity);
        return entityToDto(saved);
    }

    @Override
    public TDto updateDto(TDto dto) {
        String id;
        try {
            id = (String) dto.getClass().getMethod("getId").invoke(dto);
        } catch (Exception e) {
            throw new DataNotFoundException();
        }
        TEntity targetEntity = this.getById(id);
        if (StringUtils.isEmpty(targetEntity)) {
            throw new DataNotFoundException();
        } else {
//            targetEntity = (TEntity) BeanCopyUtils.copyBean(dto, targetEntity);
            targetEntity = dtoToEntity(dto, targetEntity);
            TEntity saved = repository.saveAndFlush(targetEntity);
            return entityToDto(saved);
        }
    }

    @Override
    public TDto getDtoById(String id) {
        TEntity entity = getById(id);
        return entityToDto(entity);
    }

    @Override
    public List<TDto> getAllDtos() {
        List<TDto> dtos = getAll().stream().map(this::entityToDto).collect(Collectors.toList());
        return dtos;
    }

    @Override
    public List<TDto> getList(Specification<TEntity> specification, Sort sort) {
        if (ObjectUtils.isEmpty(sort)) {
            return repository.findAll(specification).stream().map(this::entityToDto).collect(Collectors.toList());
        }
        return repository.findAll(specification, sort)
                .stream().map(this::entityToDto).collect(Collectors.toList());
    }

    @Override
    public Page<TDto> getPagedDtos(Specification<TEntity> specification, Pageable pageable) {
        Page<TEntity> entities = getPaged(specification, pageable);
        Page<TDto> dtos = entities.map(this::entityToDto);
        return dtos;
    }

    @Override
    public Page<TDto> getListByPredicate(Predicate predicate, Pageable pageable) {
        if (ObjectUtils.isEmpty(predicate)) {
            throw new BizException("缺少参数");
        }
        return repository.findAll(predicate, pageable).map(this::entityToDto);
    }

    @Override
    public TDto saveDto(TDto dto) {
        TEntity entity = dtoToEntity(dto);
        entity = (TEntity) recurseChildrenParent(entity);
        TEntity saved = save(entity);
        return entityToDto(saved);
    }
}
